/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\mwinpcap.h"

//---------------------------------------------------------------------------

MWinPcap::MWinPcap(void)
{
	intfName.Empty();
	friendlyName.Empty();
	captureHandle = NULL;

	intfInfoCnt = 0;

	defaultInterfaceName.Empty();
}

MWinPcap::~MWinPcap(void)
{
	EndCapture();
}

//---------------------------------------------------------------------------

void MWinPcap::PopulateInterfaceInfo(pcap_if_t* devsIn, short familyIn)
{
	pcap_if_t    *dev;
	pcap_addr    *addr;
	sockaddr     *sa;
	sockaddr_in  *sa4;
	sockaddr_in6 *sa6;
	CString      str;
	int idx, i;

	if(devsIn==NULL) return;

	idx = 0;
	dev = devsIn;
	while(dev!=NULL && idx<MAX_MIP_INTERFACE_INFO){
		addr = dev->addresses;
		while(addr!=NULL){
			sa = addr->addr;
			if(sa!=NULL){
				if(sa->sa_family==familyIn){
					intfInfo[idx].intfName = dev->name;
					intfInfo[idx].intfDesc = dev->description;
					if(sa->sa_family==AF_INET){
						sa4 = (sockaddr_in*)sa;
						intfInfo[idx].addrIP.S_un.S_addr = sa4->sin_addr.S_un.S_addr;
					}
					if(sa->sa_family==AF_INET6){
						sa6 = (sockaddr_in6*)sa;
						for(i=0; i<8; i++)
							intfInfo[idx].addrIPv6.u.Word[i] = sa6->sin6_addr.u.Word[i];
					}
					idx++;
				}
			}
			addr = addr->next;
		}
		dev = dev->next;
	}

	intfInfoCnt = idx;
}

bool MWinPcap::GetIntfDevice(short familyIn)
{
	pcap_if_t *alldevs;
	bool found;
	int res, i;

	intfName.Empty();
	friendlyName.Empty();

	memset(errbuf, 0, PCAP_ERRBUF_SIZE);
	res = pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf);
	if(res==-1) return false;
	if(alldevs==NULL) return false;

	PopulateInterfaceInfo(alldevs, familyIn);

	if(intfInfoCnt==0){
		// NO usable interface found
		pcap_freealldevs(alldevs);
		return false;
	}

	if(intfInfoCnt==1){
		// one interface found, setting default to this one
		defaultInterfaceName = intfInfo[0].intfName;
		intfName             = intfInfo[0].intfName;
		friendlyName         = intfInfo[0].intfDesc;
	}
	else{
		// more the one, search for default
		found = false;
		for(i=0; i<intfInfoCnt && !found; i++){
			if(defaultInterfaceName.CompareNoCase(intfInfo[i].intfName)==0){
				intfName     = intfInfo[i].intfName;
				friendlyName = intfInfo[i].intfDesc;
				found = true;
			}
		}

		if(!found){
			// default one NOT found, pick the first one
			defaultInterfaceName = intfInfo[0].intfName;
			intfName             = intfInfo[0].intfName;
			friendlyName         = intfInfo[0].intfDesc;
		}
	}

	pcap_freealldevs(alldevs);

	if(!ipHelper.FindAdapter(intfName)){
		intfName.Empty();
		friendlyName.Empty();
		return false;
	}

	return true;
}

bool MWinPcap::FindAddressFamily(pcap_addr *addressIn, short familyIn)
{
pcap_addr *address;
sockaddr_in *sAddr;
bool found;

	if(addressIn==NULL) return false;

	found = false;
	address = addressIn;
	while(address!=NULL && !found){
		sAddr = (sockaddr_in*)address->addr;
		if(sAddr!=NULL){
			if(familyIn==sAddr->sin_family)
				found = true;
		}
	
		address = address->next;
	}

	return found;
}

bool MWinPcap::GetIpInterface(void)
{
	return GetIntfDevice(AF_INET);
}

CString MWinPcap::GetIpInterfaceName(void)
{
	return intfName;
}

CString MWinPcap::GetIpInterfaceAdapterName(void)
{
	return friendlyName;
}

int MWinPcap::GetIpInterfaceCount(void)
{
	return intfInfoCnt;
}

MIpInterfaceInfo* MWinPcap::GetIpInterfaceInfo(int idx)
{
	if(idx<0) return NULL;
	if(idx>=intfInfoCnt) return NULL;

	return &intfInfo[idx];
}

//---------------------------------------------------------------------------

DWORD MWinPcap::GetLocalIP(void)
{
	return ipHelper.ipAddress.S_un.S_addr;
}

DWORD MWinPcap::GetLocalIPMask(void)
{
	return ipHelper.ipMask.S_un.S_addr;
}

DWORD MWinPcap::GetLocalIPBroadcastAddress(void)
{
	DWORD res, mask;

	mask = ~ipHelper.ipMask.S_un.S_addr;

	res = ipHelper.ipAddress.S_un.S_addr | mask;

	return res;
}

DWORD MWinPcap::GetGatewayIP(void)
{
	return ipHelper.ipAddrGw.S_un.S_addr;
}

DWORD MWinPcap::GetDnsServerIP(void)
{
	return ipHelper.ipAddrDns.S_un.S_addr;
}

bool MWinPcap::IpAddrBelongsToLocalNet(DWORD addrIn)
{
	return ipHelper.IpAddrBelongsToLocalNet(addrIn);
}

//---------------------------------------------------------------------------

bool MWinPcap::BeginCapture(void)
{
	return BeginCapture(1000);
}

bool MWinPcap::BeginCapture(int timeOutIn)
{
	int readTimeOut;

	EndCapture();

	if(intfName.IsEmpty()) return false;

	readTimeOut = timeOutIn;
	if(readTimeOut<1)
		readTimeOut = 1;

	memset(errbuf, 0, PCAP_ERRBUF_SIZE);
	captureHandle = pcap_open(
						intfName,					// name of the device
						1024,						// portion of the packet to capture (only the first 1024 bytes)
						PCAP_OPENFLAG_PROMISCUOUS,	// promiscuous mode
						readTimeOut,				// read timeout
						NULL,						// authentication on the remote machine
						errbuf
						);
	if(captureHandle==NULL) return false;

	return true;
}

//---------------------------------------------------------------------------
void MWinPcap::EndCapture(void)
{

	if(captureHandle!=NULL){
		pcap_close(captureHandle);
		captureHandle = NULL;
	}
}
//---------------------------------------------------------------------------
int MWinPcap::CaptureOnePacket(BYTE* buffData, int* buffSize)
{
struct pcap_pkthdr  *header;
const unsigned char *data;
int retVal;
int res;
int size;
unsigned int usize;

	if(captureHandle==NULL)
		return -3;
	if(buffData==NULL)
		return -3;
	if(buffSize==NULL)
		return -3;

	size = *buffSize;
	if(size<=0)
		return -3;

	// Retrieve one packet
	res = pcap_next_ex(captureHandle, &header, &data);
	if(res<0)
		return -3;
	if(res==0){
		// timed out
		*buffSize = 0;
		return 0;
	}

	if(header==NULL || data==NULL){
		// invalid return values
		*buffSize = 0;
		return -3;
	}

/*
	if(header->caplen != header->len){
		// fragmented
		*buffSize = 0;
		return -2;
	}
*/

	usize = (unsigned int)size;

	if(usize < header->len){
		retVal = -1;
	}
	else retVal = 1;

	if(usize > header->len)
		usize = header->len;

	memcpy(buffData, data, usize);
	*buffSize = usize;

	return retVal;
}


//---------------------------------------------------------------------------

void MWinPcap::FillSrcMAC(BYTE* buff)
{
	if(buff==NULL) return;
	memcpy(buff, ipHelper.macAddress, 6);
}

void MWinPcap::FillSrcIp(BYTE* buff)
{
	if(buff==NULL) return;
	buff[0] = ipHelper.ipAddress.S_un.S_un_b.s_b1;
	buff[1] = ipHelper.ipAddress.S_un.S_un_b.s_b2;
	buff[2] = ipHelper.ipAddress.S_un.S_un_b.s_b3;
	buff[3] = ipHelper.ipAddress.S_un.S_un_b.s_b4;
}

//---------------------------------------------------------------------------

void MWinPcap::FillDnsMAC(BYTE* buff)
{
	if(buff==NULL) return;
	memcpy(buff, ipHelper.macDnsServer, 6);
}

void MWinPcap::SetDnsMAC(BYTE* buff)
{
	if(buff==NULL) return;
	memcpy(ipHelper.macDnsServer, buff, 6);
}

//---------------------------------------------------------------------------

void MWinPcap::GetIPv6UnicastAddress(in_addr6* addrIn)
{
	ipHelper.GetIPv6UnicastAddress(addrIn);
}

void MWinPcap::GetIPv6AnycastAddress(in_addr6* addrIn)
{
	ipHelper.GetIPv6AnycastAddress(addrIn);
}

void MWinPcap::GetIPv6MulticastAddress(in_addr6* addrIn)
{
	ipHelper.GetIPv6MulticastAddress(addrIn);
}

void MWinPcap::GetIPv6DNSAddress(in_addr6* addrIn)
{
	ipHelper.GetIPv6DNSAddress(addrIn);
}

//---------------------------------------------------------------------------

bool MWinPcap::SendPacket(BYTE* dataIn, int sizeIn)
{
	int res;

	if(dataIn==NULL)        return false;
	if(sizeIn<=0)           return false;
	if(captureHandle==NULL) return false;

	res = pcap_sendpacket(captureHandle, dataIn, sizeIn);

	return res==0 ? true : false;
}

//---------------------------------------------------------------------------
